﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Net;
using System.ComponentModel;
using System.Security.Cryptography;

namespace Continuum.Classes
{
    public class MarketEngine
    {
        public List<MarketFund> MarketFunds = new List<MarketFund>();

        private bool _AutoRefresh = false;
        [Description("")]
        public bool AutoRefresh { get { return _AutoRefresh; } set { _AutoRefresh = value; } }

        private DateTime _LastRefreshed = DateTime.MinValue;
        [Description("")]
        public DateTime LastRefreshed { get { return _LastRefreshed; } set { _LastRefreshed = value.Date; } }

        private int _RefreshDays = 1;
        [Description("")]
        public int RefreshDays { get { return _RefreshDays; } set { _RefreshDays = value; } }

        #region Serialization

        public static MarketEngine LoadMarket(string Filename)
        {
            MarketEngine newSettings;

            XmlSerializer s = new XmlSerializer(typeof(MarketEngine));
            TextReader r = new StreamReader(Filename);
            newSettings = (MarketEngine)s.Deserialize(r);
            r.Close();

            return newSettings;
        }

        // Encryption is derived from samples here :
        // http://www.christec.co.nz/blog/archives/81
        // http://www.discussweb.com/other-web-programming-languages/2091-uses-symmetric-key-algorithm-rijndael-encrypt-decrypt-data.html
        public static MarketEngine LoadMarket(string Filename, string Password)
        {
            MarketEngine newSettings;
            XmlSerializer s = new XmlSerializer(typeof(MarketEngine));

            byte[] rgbIV = System.Text.ASCIIEncoding.UTF8.GetBytes("1150845670772664");  // some random number
            byte[] saltValueBytes = Encoding.ASCII.GetBytes("s0d1uMv4l");

            // Configure the desired encryption algorithm parameters
            SymmetricAlgorithm sa = new RijndaelManaged();

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, saltValueBytes, "SHA1", 2);

            // Use the password to generate pseudo-random bytes for the encryption
            // key. Specify the size of the key in bytes (instead of bits).
            byte[] keyBytes = pdb.GetBytes(256 / 8);

            sa.Mode = CipherMode.CBC;

            // Decryption example
            ICryptoTransform transform = sa.CreateDecryptor(keyBytes, rgbIV);
            using (Stream inputStream = new FileStream(Filename, FileMode.Open))
            {
                // Wrap the input stream up with a CryptoStream
                // which performs the data decryption
                using (Stream cryptoStream = new CryptoStream(inputStream, transform, CryptoStreamMode.Read))
                {
                    // Read data into the cryptoStream (which will fetch encrypted
                    // data from the inputStream and then decrypt it before returning
                    // it to us)
                    using (TextReader tr = new StreamReader(cryptoStream))
                        newSettings = (MarketEngine)s.Deserialize(tr); ;
                }
            }

            return newSettings;
        }

        public string SaveMarket()
        {
            string Filename = MainForm.ConfigDir + MainForm.projector.UniverseName + ".market";

            XmlSerializer s = new XmlSerializer(this.GetType());
            TextWriter w = new StreamWriter(Filename);
            s.Serialize(w, this);
            w.Close();

            return Filename;
        }

        // Encryption is derived from samples here :
        // http://www.christec.co.nz/blog/archives/81
        // http://www.discussweb.com/other-web-programming-languages/2091-uses-symmetric-key-algorithm-rijndael-encrypt-decrypt-data.html
        public string SaveMarket(string Password)
        {
            string Filename = MainForm.ConfigDir + MainForm.projector.UniverseName + ".marketx";
            XmlSerializer s = new XmlSerializer(this.GetType());
            //byte[] rgbKey = System.Text.ASCIIEncoding.UTF8.GetBytes(Password);
            byte[] rgbIV = System.Text.ASCIIEncoding.UTF8.GetBytes("1150845670772664");  // some random number
            byte[] saltValueBytes = Encoding.ASCII.GetBytes("s0d1uMv4l");

            // Configure the desired encryption algorithm parameters
            SymmetricAlgorithm sa = new RijndaelManaged();

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, saltValueBytes, "SHA1", 2);

            // Use the password to generate pseudo-random bytes for the encryption
            // key. Specify the size of the key in bytes (instead of bits).
            byte[] keyBytes = pdb.GetBytes(256 / 8);


            sa.Mode = CipherMode.CBC;

            // Encryption Example
            ICryptoTransform transform = sa.CreateEncryptor(keyBytes, rgbIV);
            using (Stream outputStream = new FileStream(Filename, FileMode.Create))
            {
                // Wrap the output stream up with a CryptoStream
                // which performs the data encryption
                using (Stream cryptoStream = new CryptoStream(outputStream, transform, CryptoStreamMode.Write))
                {
                    // Store data into the cryptoStream (which will encrypt it
                    // and then pass it along to our outputStream for storage)
                    using (TextWriter tw = new StreamWriter(cryptoStream))
                    {
                        s.Serialize(tw, this);
                    }
                }
            }

            return Filename;
        }

        #endregion

        #region Basic WebFetch

        public static string FetchPage(string url)
        {
            try
            {
                // used to build entire input
                StringBuilder sb = new StringBuilder();

                // used on each read operation
                byte[] buf = new byte[8192];

                // prepare the web page we will be asking for
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

                // execute the request
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                // we will read data via the response stream
                Stream resStream = response.GetResponseStream();

                string tempString = null;
                int count = 0;
                int bytesread = 0;

                do
                {
                    // fill the buffer with data
                    count = resStream.Read(buf, 0, buf.Length);

                    // make sure we read some data
                    if (count != 0)
                    {
                        // translate from bytes to ASCII text
                        tempString = Encoding.ASCII.GetString(buf, 0, count);

                        // continue building the string
                        sb.Append(tempString);

                        bytesread += count;
                    }
                }
                while (count > 0); // any more data to read?

                return sb.ToString();
            }
            catch (Exception ex)
            {
                return "Error";
            }
        }

        #endregion

        #region Yahoo Webservice Methods

        public Decimal GetSymbolValue(string MarketSymbol)
        {
            MainForm.InstanceRef.txtConsole.Text += "Refreshing " + MarketSymbol + " : " + DateTime.Now.ToLongTimeString() + "\r\n";
            string Result = FetchPage("http://download.finance.yahoo.com/d/quotes.csv?s=" + MarketSymbol + "&f=l1");
            MainForm.InstanceRef.txtConsole.Text += "Done refresh of " + MarketSymbol + " : " + DateTime.Now.ToLongTimeString() + "\r\n";

            if (Result == "Error") return Decimal.MinValue;

            try
            {
                Decimal UnitPrice = Decimal.Parse(Result);
                return UnitPrice;
            }
            catch
            {
                return Decimal.MinValue;
            }
        }

        public bool WebRefresh()
        {
            //if (AutoRefresh == false) return false;

            MainForm.InstanceRef.txtConsole.Text += "Webrefresh started at : " + DateTime.Now.ToLongTimeString() + "\r\n";

            TimeSpan ts = DateTime.Now.Date - LastRefreshed;

            if ((LastRefreshed == DateTime.MinValue) || 
                 (LastRefreshed != DateTime.MinValue && ts.Days >= RefreshDays) )
            {
                foreach (MarketFund marketfund in MarketFunds)
                {
                    Decimal UnitPrice = GetSymbolValue(marketfund.MarketSymbol);

                    if (UnitPrice != Decimal.MinValue)
                    {
                        Balance bal = new Balance(UnitPrice, DateTime.Now.Date, "Web Refresh");
                        marketfund.HistoricalBalances.Add(bal);
                    }
                }

                LastRefreshed = DateTime.Now;

                MainForm.InstanceRef.txtConsole.Text += "Webrefresh ended at : " + DateTime.Now.ToLongTimeString() + "\r\n";

                return true;
            }

            MainForm.InstanceRef.txtConsole.Text += "Webrefresh not needed : " + DateTime.Now.ToLongTimeString() + "\r\n";

            return false;
        }

        public bool DownloadHistory(MarketFund marketfund, int SampleRate, DateTime StartDate, DateTime EndDate)
        {
            string HistoryText = "";

            try
            {
                // 12/20/200
                // 4/28/2009
                //HistoryText = FetchPage("http://ichart.finance.yahoo.com/table.csv?s=FFFFX&d=3&e=28&f=2009&g=d&a=11&b=20&c=2000&ignore=.csv");
                HistoryText = FetchPage("http://ichart.finance.yahoo.com/table.csv?s=" + marketfund.MarketSymbol + "&d=" + (EndDate.Month - 1).ToString() + "&e=" + EndDate.Day.ToString() + "&f=" + EndDate.Year.ToString() + "&g=d&a=" + (StartDate.Month-1).ToString() + "&b=" + StartDate.Day.ToString() + "&c=" + StartDate.Year.ToString() + "&ignore=.csv");
            }
            catch (Exception ex)
            {
                return false;
            }

            int SampleCount = SampleRate - 1;

            // This process will clear old balances
            marketfund.HistoricalBalances.Clear();

            try
            {
                string[] Lines = HistoryText.Split('\n');
                foreach (string Line in Lines)
                {
                    // Last row is blank
                    if (Line.Trim() == "") continue;

                    string[] cols = Line.Split(',');

                    // First row is column headers
                    if (cols[0] == "Date") continue;

                    if (SampleCount <= 0)
                    {
                        DateTime date = DateTime.Parse(cols[0]);
                        Decimal closeval = Decimal.Parse(cols[4]);

                        Balance bal = new Balance(closeval, date, "yahoo quote");
                        marketfund.HistoricalBalances.Add(bal);

                        SampleCount = SampleRate - 1;
                    }
                    else
                    {
                        SampleCount--;
                    }
                }
            }
            catch
            {
                return false;
            }

            marketfund.HistoricalBalances.Sort(new BalanceComparer());
            LastRefreshed = DateTime.MinValue;

            return true;
        }

        #endregion
    }
}
